import time
import logging
import requests
import base64
import hashlib
from datetime import datetime
from typing import Optional, Dict
from tenacity import retry, stop_after_attempt, wait_exponential
from apscheduler.schedulers.background import BackgroundScheduler
from apscheduler.triggers.cron import CronTrigger
from pydantic import BaseModel, ValidationError



# --------------------------
# 基础模型验证
# --------------------------
class MessageBase(BaseModel):
    msgtype: str
    image: Optional[Dict[str, str]] = None


# --------------------------
# 安全日志格式化
# --------------------------
class SafeFormatter(logging.Formatter):
    def format(self, record):
        message = super().format(record)
        if 'webhook/send?key=' in message:
            message = message.split('webhook/send?key=')[0] + 'webhook/send?key=***'
        return message


# --------------------------
# 同步企业微信图片机器人
# --------------------------
class SyncQyWechatBot:
    def __init__(self, config):
        self.config = config
        webhook_key = self.config['qywxapi']['webhook_key']
        self.webhook_url = f"https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key={webhook_key}"
        self.scheduler = BackgroundScheduler()
        self.scheduler.configure(
            job_defaults={
                'coalesce': True,  # 合并积压任务
                'max_instances': 1,  # 防止并发过高
                'misfire_grace_time': None
            }
        )
        self._setup_logging()
        self.scheduler.start()

    def _setup_logging(self):
        """日志配置"""
        self.logger = logging.getLogger('SyncQyWechatBot')
        self.logger.setLevel(logging.INFO)
        formatter = SafeFormatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
        ch = logging.StreamHandler()
        ch.setFormatter(formatter)
        self.logger.addHandler(ch)
        self.logger.propagate = False

    @retry(
        stop=stop_after_attempt(3),
        wait=wait_exponential(multiplier=1, min=5, max=10)
    )
    def send_image(self, image_path: str):
        """同步发送图片"""
        start_time = time.perf_counter()  # 发送耗时记录
        try:
            # 处理并验证图片
            image_data = self._process_image(image_path)
            message = MessageBase(
                msgtype="image",
                image=image_data
            )

            # 验证消息格式
            if not self._validate_message(message):
                return False

            # 同步发送请求
            response = requests.post(
                self.webhook_url,
                json=message.model_dump(),
                timeout=10
            )
            result = response.json()

            if result.get('errcode') != 0:
                error_msg = f"发送失败: {result['errmsg']}({result['errcode']})"
                self.logger.error(error_msg)
                raise Exception(error_msg)

            self.logger.info("图片发送成功")
            return True

        except Exception as e:
            self.logger.error(f"发送失败: {str(e)}")
            raise
        finally:
            elapsed = time.perf_counter() - start_time
            self.logger.debug(f"发送耗时: {elapsed:.3f}s")
            if elapsed > 5.0:  # 超时警告
                self.logger.warning("发送响应时间过长")

    def _process_image(self, image_path: str) -> Dict[str, str]:
        """处理图片编码"""
        with open(image_path, "rb") as f:
            image_data = f.read()

        if len(image_data) > 2 * 1024 * 1024:
            raise ValueError("图片大小超过2MB限制")

        return {
            "base64": base64.b64encode(image_data).decode('utf-8'),
            "md5": hashlib.md5(image_data).hexdigest()
        }

    def _validate_message(self, message: MessageBase) -> bool:
        """消息格式验证"""
        try:
            MessageBase(**message.model_dump())
            return True
        except ValidationError as e:
            self.logger.error(f"消息格式错误: {str(e)}")
            return False

    # --------------------------
    # 定时任务管理
    # --------------------------
    def schedule_image_task(self, image_path: str, schedule_config: dict):
        """
        安排定时发图任务
        :param image_path: 图片路径
        :param schedule_config: 调度配置
        """
        trigger = CronTrigger(**schedule_config)
        self.scheduler.add_job(
            self.send_image,
            trigger=trigger,
            args=[image_path],
            id=f"image_task_{image_path}",
            replace_existing=True,
            next_run_time=datetime.now()
        )
        self.logger.info(f"图片定时任务已注册: {image_path}")

    def shutdown(self):
        """安全关闭"""
        self.scheduler.shutdown()
        self.logger.info("系统已关闭")


# --------------------------
# 使用示例
# --------------------------
# if __name__ == "__main__":
#     config = SecureConfigLoader.load("config.yaml")
#     bot = SyncQyWechatBot(config)
#
#     # 同步发送测试
#     try:
#         bot.send_image("./screenshots/screenshot.png")
#     except Exception as e:
#         print(f"发送失败: {str(e)}")
#
#     # 定时任务示例
#     bot.schedule_image_task(
#         image_path="./screenshots/daily_report.png",
#         schedule_config={'hour': 9, 'minute': 0}  # 每天9点发送
#     )
#
#     try:
#         while True:
#             time.sleep(1)
#     except KeyboardInterrupt:
#         bot.shutdown()